#coding=utf-8
import base64
import datetime
import hashlib
import json
import os
import random
import time
import traceback
import re

import numpy
import requests
from loguru import logger

class DY:
    def __init__(self, host, playwright, app_id, need_check_store_id):
        self.playwright = playwright
        self.secret = "P4I8HCOP1OP7432XZVKR2MPJKKVQ7J"
        self.context = None
        self.browser = None
        self.page = None
        self.host = host
        self.index_url = "https://fxg.jinritemai.com/login/common"
        self.app_id = app_id
        self.tj_account = self.get_tujian_account()
        # 是否需要检查店铺ID
        self.need_check_store_id = need_check_store_id

    def login(self, account_obj: dict):
        logger.info("操作抖音账号信息：【{}】店铺名：【{}】".format(account_obj['userName'], account_obj['account']))
        browser = self.playwright.chromium.launch(headless=False)
        self.context = browser.new_context(accept_downloads=True)
        self.page = self.context.new_page()
        self.page.goto(self.index_url, timeout=60000)
        self.wait(self.page)
        logger.info("点击邮箱登录...")
        self.page.click('//*[@id="ecomLoginForm"]/section/div[2]/div[2]')
        logger.info("输入账号...")
        self.page.query_selector('//*[@id="ecomLoginForm"]/section/div[3]/div[1]/div/div/span/input').fill(
            account_obj['userName'])
        logger.info("输入密码...")
        self.page.query_selector('//*[@id="ecomLoginForm"]/section/div[3]/div[2]/div/div/span/input').fill(
            account_obj['pwd'])
        logger.info("勾选协议...")
        self.page.click('//*[@id="root"]/div/div[1]/div/div/div/div/div[2]/div[4]/div[1]/label/span/input')
        logger.info("点击登录...")
        self.page.click('//*[@id="ecomLoginForm"]/section/div[6]/button')
        self.wait(self.page)
        self.slide_code()
        logger.info("检查是否滑动成功...")
        self.check_sms_code(account_obj['mobile'])
        time.sleep(1)
        # 调用一次，可能有活动验证码加载不出来
        self.page.query_selector('div')
        logger.info('再次检测有没有滑动验证码')
        self.slide_code()
        if self.page.query_selector('div:has-text("请选择店铺")') is not None and self.page.query_selector(
                'div:has-text("请选择店铺")').is_visible():
            logger.info("登录成功...")
        else:
            raise Exception("登录失败...")

    def download_fund_flow(self, account_obj: dict, yesterday):
        logger.info("下载资金流水...")
        logger.info("点击【资金】下的【账单管理】...")
        has_menu = False
        try:
            self.page.query_selector('div[data-nf-scroll="menu"] >> a:text-is("账单管理")').click()
            self.wait(self.page)
            self.close_tips()
            logger.info('点击资金账单...')
            self.page.query_selector('div.auxo-tabs-tab:has-text("资金账单")').click()
            for t in range(30):
                try:
                    if self.page.query_selector('span[title="动账时间"]:visible') is not None:
                        break
                    time.sleep(1)
                    logger.info('等待页面加载...')
                except:
                    pass
            has_menu = True
        except:
            title_list = self.page.query_selector_all('a.style_title__1KAmO')
            for item in title_list:
                if item.text_content() == '资金流水明细':
                    item.click()
                    logger.info("点击【资金】下的【资金流水明细】成功...")
                    self.wait(self.page)
                    has_menu = True
                    break
            self.close_tips()
        if not has_menu:
            return -1
        time.sleep(2)
        logger.info("点击动账时间...")
        yesterday = yesterday.replace('/', '-')
        self.page.click('input[placeholder="开始日期"]')
        time.sleep(1)
        self.page.keyboard.press('Control+A')
        time.sleep(0.5)
        self.page.keyboard.press('Backspace')
        time.sleep(0.5)
        self.page.fill('input[placeholder="开始日期"]', yesterday)
        time.sleep(1)
        self.page.keyboard.press('Enter')
        time.sleep(1)
        self.page.keyboard.press('Control+A')
        time.sleep(0.5)
        self.page.keyboard.press('Backspace')
        time.sleep(0.5)
        self.page.fill('input[placeholder="结束日期"]', yesterday)
        time.sleep(1)
        self.page.keyboard.press('Enter')
        # 激活窗口
        logger.info("点击【动账账户】关闭动账时间的选择框...")
        self.page.click('//div[text()="动账账户"]')
        time.sleep(2)
        logger.info("点击查询")
        with self.page.expect_response('*bill_center/domestic/shop/query_item*') as response:
            self.page.query_selector("button:has-text('查询')").click()
        res = response.value
        if res.json()['total'] == 0:
            logger.info('暂无数据')
            return None
        time.sleep(3)
        logger.info("点击【生成报表】按钮...")
        self.page.query_selector('//span[text()="生成报表"]').click()
        logger.info("再点击【生成...】")
        with self.page.expect_response("*bill_center/domestic/shop/download_item*") as response:
            self.page.query_selector('//span[text()="生成"]').click()
        time.sleep(2)
        self.wait(self.page)
        for i in range(60):
            if self.page.query_selector('button:has-text("下载设置"):visible') is not None:
                break
            time.sleep(1)
        time.sleep(5)
        first_li = None
        for i in range(60):
            li_list = self.page.query_selector_all('li.cardItem-vzzklb')
            if len(li_list) == 0:
                li_list = self.page.query_selector_all('li.vB6eemtMuR1K81DVZU6B')
            first_li = li_list[0]
            if first_li.query_selector('//span[text()="下载"]') is None:
                logger.info("下载按钮还没有刷新出来，重新加载页面...")
                self.page.reload()
                self.wait(self.page)
                time.sleep(5)
                self.close_tips()
            else:
                logger.info("找到下载按钮...")
                break
        if "动账时间{}".format(yesterday.replace('-', '/')) in first_li.text_content():
            logger.info("找到昨天的资金流水明细...")
            logger.info("正在点击下载按钮...")
            with self.page.expect_download() as download_info:
                first_li.query_selector('//span[text()="下载"]').click()
            download_obj = download_info.value
            file_dir = os.path.join(os.getcwd(), "temp")
            if not os.path.exists(file_dir):
                os.mkdir(file_dir)
            timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
            file_path = os.path.join(file_dir, "{}-{}-{}-{}.csv".format("抖音-资金流水明细", account_obj['account'], yesterday.replace('-', ''), timestamp))
            if os.path.exists(file_path):
                os.remove(file_path)
            download_obj.save_as(file_path)
            # 等待文件下载完成
            for i in range(60):
                if os.path.exists(file_path):
                    break
                time.sleep(1)
            logger.info("店铺【{}】文件已经下载：【{}】".format(account_obj['account'], file_path))
            return file_path
        raise Exception("下载文件失败")

    def get_bill_count(self, period):
        logger.info("点击【资金】下的【账单管理】...")
        has_menu = False
        try:
            self.page.query_selector('div[data-nf-scroll="menu"] >> a:text-is("账单管理")').click()
            self.wait(self.page)
            has_menu = True
            self.page.query_selector('div.auxo-tabs-tab-btn:has-text("结算账单")').click()
            self.wait(self.page)
        except:
            title_list = self.page.query_selector_all('a.style_title__1KAmO')
            for item in title_list:
                if item.text_content() == '结算账单':
                    item.click()
                    logger.info("点击【资金】下的【结算账单】成功...")
                    self.wait(self.page)
                    has_menu = True
                    break
        if not has_menu:
            return '-'
        time.sleep(2)
        self.close_tips()
        logger.info("输入【结算日期】...")
        self.page.click('input[placeholder="开始日期"]')
        time.sleep(1)
        start_date = period.replace('/', '-') + '-01'
        self.page.fill('input[placeholder="开始日期"]', start_date)
        time.sleep(1)
        # 按tab键
        self.page.keyboard.press('Enter')
        time.sleep(1)
        end_date = ((datetime.datetime.strptime(start_date, '%Y-%m-%d') + datetime.timedelta(days=31)).replace(day=1) - datetime.timedelta(days=1)).strftime('%Y-%m-%d')
        self.page.fill('input[placeholder="结束日期"]', end_date)
        time.sleep(1)
        self.page.keyboard.press('Enter')
        time.sleep(1)
        logger.info("点击查询...")
        self.close_tips()
        try:
            with self.page.expect_response('*shop/query_shop_settle_order*') as response:
                self.page.query_selector('//span[text()="查询"]').click()
            res = response.value
            return res.json()['total']
        except:
            with self.page.expect_response('https://fxg.jinritemai.com/bill_center/domestic/shop/query_item*') as response:
                self.page.query_selector('//span[text()="查询"]').click()
            res = response.value
            return res.json()['total']

    def get_fund_flow_count(self, period):
        logger.info("点击【资金】下的【账单管理】...")
        has_menu = False
        try:
            self.page.query_selector('div[data-nf-scroll="menu"] >> a:text-is("账单管理")').click()
            self.wait(self.page)
            self.close_tips()
            logger.info('点击资金账单...')
            self.page.query_selector('div.auxo-tabs-tab:has-text("资金账单")').click()
            for t in range(30):
                try:
                    if self.page.query_selector('span[title="动账时间"]:visible') is not None:
                        break
                    time.sleep(1)
                    logger.info('等待页面加载...')
                except:
                    pass
            has_menu = True
        except:
            title_list = self.page.query_selector_all('a.style_title__1KAmO')
            for item in title_list:
                if item.text_content() == '资金流水明细':
                    item.click()
                    logger.info("点击【资金】下的【资金流水明细】成功...")
                    self.wait(self.page)
                    has_menu = True
                    break
            self.close_tips()
        if not has_menu:
            return '-'
        time.sleep(2)
        logger.info("点击动账时间...")
        self.page.click('input[placeholder="开始日期"]')
        time.sleep(1)
        self.page.keyboard.press('Control+A')
        time.sleep(0.5)
        self.page.keyboard.press('Backspace')
        time.sleep(0.5)
        start_date = period.replace('/', '-') + '-01'
        self.page.fill('input[placeholder="开始日期"]', start_date)
        time.sleep(1)
        self.page.keyboard.press('Enter')
        time.sleep(1)
        self.page.keyboard.press('Control+A')
        time.sleep(0.5)
        self.page.keyboard.press('Backspace')
        time.sleep(0.5)
        end_date = ((datetime.datetime.strptime(start_date, '%Y-%m-%d') + datetime.timedelta(days=31)).replace(day=1) - datetime.timedelta(days=1)).strftime('%Y-%m-%d')
        self.page.fill('input[placeholder="结束日期"]', end_date)
        time.sleep(1)
        self.page.keyboard.press('Enter')
        # 激活窗口
        logger.info("点击【动账账户】关闭动账时间的选择框...")
        self.page.click('//div[text()="动账账户"]')
        time.sleep(2)
        logger.info("点击查询")
        with self.page.expect_response('*shop/be/settlement/settlement/shopAccountBillItems*') as response:
            self.page.query_selector("button:has-text('查询')").click()
        res = response.value
        return res.json()['total']

    # 登录之后检测弹窗
    def after_login_check_dialog(self):
        self.wait(self.page)
        time.sleep(10)
        # self.page.reload()
        # self.wait(self.page)
        # time.sleep(5)
        # self.page.wait_for_url(url="https://fxg.jinritemai.com/ffa/mshop/homepage/index")
        self.page.expect_navigation(url="https://fxg.jinritemai.com/ffa/mshop/homepage/index")
        time.sleep(5)
        # 关闭浏览器兼容弹窗
        self.close_browser_tip()
        time.sleep(5)
        if self.page.query_selector('div[data-log_element_key="modal_campaign_shop_push_self_cancel"]:visible') is not None:
            self.page.query_selector('div[data-log_element_key="modal_campaign_shop_push_self_cancel"]:visible').click()
        time.sleep(1)
        tips = self.page.query_selector_all('//span[text()="我已知悉"]')
        for tip in tips:
            if tip.is_visible():
                try:
                    tip.click(timeout=2000)
                except:
                    logger.info("点击【{}】按钮失败...".format(tip.text_content()))
        time.sleep(1)
        # 关闭广告弹窗
        for c in range(3):
            self.close_eve_dialog()
            self.close_package_dialog()
            self.close_level_dialog()
            self.close_ad_dialog()
            self.click_sign_dialog()
            self.click_adjust_dialog()
        time.sleep(5)

    # 下载结算账单
    def download_bill(self, account_obj, yesterday, start_time):
        # 获取余额
        self.get_balance(account_obj['storeId'], start_time.split(' ')[0])
        logger.info("点击【资金】下的【账单管理】...")
        has_menu = False
        try:
            self.page.query_selector('div[data-nf-scroll="menu"] >> a:text-is("账单管理")').click()
            self.wait(self.page)
            has_menu = True
            self.page.query_selector('div.auxo-tabs-tab-btn:has-text("结算账单")').click()
            self.wait(self.page)
        except:
            title_list = self.page.query_selector_all('a.style_title__1KAmO')
            for item in title_list:
                if item.text_content() == '结算账单':
                    item.click()
                    logger.info("点击【资金】下的【结算账单】成功...")
                    self.wait(self.page)
                    has_menu = True
                    break
        if not has_menu:
            return -1
        time.sleep(2)
        self.close_tips()
        logger.info("输入【结算日期】...")
        self.page.click('input[placeholder="开始日期"]')
        time.sleep(1)
        self.page.fill('input[placeholder="开始日期"]', yesterday.replace('/', '-'))
        time.sleep(1)
        # 按tab键
        self.page.keyboard.press('Enter')
        time.sleep(1)
        self.page.fill('input[placeholder="结束日期"]', yesterday.replace('/', '-'))
        time.sleep(1)
        self.page.keyboard.press('Enter')
        time.sleep(1)
        logger.info("点击查询...")
        self.close_tips()
        try:
            with self.page.expect_response('*shop/query_shop_settle_order*') as response:
                self.page.query_selector('//span[text()="查询"]').click()
            res = response.value
            if res.json()['total'] == 0:
                logger.info('暂无数据')
                return None
        except:
            with self.page.expect_response('https://fxg.jinritemai.com/bill_center/domestic/shop/query_item*') as response:
                self.page.query_selector('//span[text()="查询"]').click()
            res = response.value
            if res.json()['total'] == 0:
                logger.info('暂无数据')
                return None
        logger.info("点击【生成报表】按钮...")
        self.page.query_selector('//span[text()="生成报表"]').click()
        logger.info("再点击【生成...】")
        with self.page.expect_response("*settlement/settlement/downloadItem*") as response:
            self.page.query_selector('//span[text()="生成"]').click()
        time.sleep(2)
        for i in range(60):
            if self.page.query_selector('button:has-text("下载设置"):visible') is not None:
                break
            time.sleep(1)
        time.sleep(5)
        first_li = None
        for i in range(60):
            li_list = self.page.query_selector_all('li.cardItem-vzzklb')
            if len(li_list) == 0:
                li_list = self.page.query_selector_all('li.vB6eemtMuR1K81DVZU6B')
            first_li = li_list[0]
            if first_li.query_selector('//span[text()="下载"]') is None:
                self.page.reload()
                self.wait(self.page)
                time.sleep(5)
            else:
                logger.info("找到下载按钮...")
                break
        if "结算时间{}".format(yesterday.replace('-', '/')) in first_li.text_content():
            logger.info("找到昨天的结算账单...")
            logger.info("正在点击下载按钮...")
            with self.page.expect_download() as download_info:
                first_li.query_selector('//span[text()="下载"]').click()
            download_obj = download_info.value
            file_dir = os.path.join(os.getcwd(), "temp")
            if not os.path.exists(file_dir):
                os.mkdir(file_dir)
            timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
            file_path = os.path.join(file_dir, "{}-{}-{}-{}.csv".format("抖音-结算账单", account_obj['account'], yesterday.replace('-', '').replace('/', ''), timestamp))
            if os.path.exists(file_path):
                os.remove(file_path)
            download_obj.save_as(file_path)
            # 等待文件下载完成
            for i in range(60):
                if os.path.exists(file_path):
                    break
                time.sleep(1)
            logger.info("文件已经下载：【{}】".format(file_path))
            return file_path
        raise Exception("下载文件失败")


    def check_store_id(self, shop_id):
        if self.need_check_store_id:
            logger.info('开始验证店铺ID...')
            for t in range(3):
                try:
                    self.page.query_selector('div.headerShopName').click()
                    time.sleep(1)
                    class_name = None
                    img_list = self.page.query_selector_all('div.auxo-popover-inner-content >> img:visible')
                    for img in img_list:
                        class_name = img.get_attribute('class')
                        if class_name is not None and 'index_headerRightMore' in class_name:
                            img.click()
                            break
                    if class_name is None:
                        continue
                    break
                except:
                    pass
            time.sleep(5)
            # 等待页面加载
            for t in range(10):
                try:
                    if self.page.query_selector('div.auxo-space-item:has-text("店铺信息"):visible') is not None:
                        break
                    time.sleep(1)
                except:
                    pass
            time.sleep(2)
            # 判断有没有权限
            if self.page.query_selector('div:has-text("抱歉，你所属的岗位没有访问该页面的权限"):visible') is not None:
                logger.info('没有权限查询店铺ID')
                return False, None
            try:
                shop_id_text = self.page.query_selector('div.ant-row:has-text("店铺ID") >> span').text_content().strip()
            except:
                shop_id_text = self.page.query_selector('div.auxo-space-item:has-text("ID")').text_content().strip().replace('ID:', '')
            logger.info(f'获取店铺ID：{shop_id_text}')
            if shop_id == shop_id_text:
                logger.info('店铺ID验证通过')
                return True, shop_id
            logger.info('店铺ID验证不通过')
            return False, shop_id_text
        return True, shop_id

    # 获取余额
    def get_balance(self, store_id, search_date):
        try:
            for k in range(3):
                logger.info('点击账户中心...')
                for c in range(3):
                    self.close_lm_dialog()
                self.page.query_selector('a:text-is("账户中心")').click()
                for c in range(5):
                    if self.page.query_selector('button:has-text("立即认证"):visible') is not None:
                        logger.info('需要实名认证')
                        return
                    time.sleep(1)
                for c in range(3):
                    self.close_price_dialog()
                    self.close_upgrade_dialog()
                    self.close_browser_tip()
                    self.close_lm_dialog()
                    self.click_sign_dialog()
                    self.click_quit_guide()
                balance_amount = self.page.query_selector('div.mt-4 >> div.flex-1:has(div:has-text("总金额")) >> div:has-text("¥")').text_content()
                self.update_balance(store_id, balance_amount, search_date)
                break
        except:
            logger.info(traceback.format_exc())

    def close_price_dialog(self):
        for i in range(3):
            try:
                if self.page.query_selector('div.disco-shape >> button:has-text("知道了"):visible') is not None:
                    self.page.query_selector('div.disco-shape >> button:has-text("知道了"):visible').click(timeout=3000)
                    break
                time.sleep(1)
            except:
                time.sleep(1)


    # 关闭升级弹窗
    def close_upgrade_dialog(self):
        for i in range(3):
            try:
                if self.page.query_selector('div.fund-control-modal-content >> button:has-text("知道了")') is not None:
                    self.page.query_selector('div.fund-control-modal-content >> button:has-text("知道了")').click(timeout=3000)
                    break
                time.sleep(1)
            except:
                time.sleep(1)


    # 关闭联盟投放弹窗
    def close_lm_dialog(self):
        for k in range(2):
            try:
                if self.page.query_selector('button[aria-label="Close"]:visible') is not None:
                    self.page.query_selector('button[aria-label="Close"]:visible').click(timeout=3000)
                    break
                time.sleep(1)
            except:
                time.sleep(1)

    # 点击退出引导弹窗
    def click_quit_guide(self):
        for i in range(3):
            try:
                if self.page.query_selector('div:text-is("退出引导"):visible') is not None:
                    self.page.query_selector('div:text-is("退出引导"):visible').click(timeout=3000)
                    break
                time.sleep(1)
            except:
                time.sleep(1)

    # 保存余额
    def update_balance(self, store_id, balance_amount, search_date):
        data = {'storeId': store_id, 'balanceAmount': balance_amount, 'billDate': search_date}
        timestamp = int(time.time() * 1000)
        text = "{}{}{}".format(self.app_id, timestamp, self.secret)
        sign = hashlib.md5(text.encode(encoding='UTF-8')).hexdigest()
        url = self.host + f"api/v1/balance/updateBalance?appId={self.app_id}&sign={sign.upper()}&timestamp={timestamp}"
        logger.info(f'url：{url}')
        logger.info(f'保存参数：{data}')
        res = requests.post(url, json=data)
        logger.info(f'保存结果：{res.text}')
        if res.status_code == 200:
            return res.json()['data']

    # 更新日志
    def update_log(self, log_id_list, message):
        data = {'logIdList': log_id_list, 'message': message}
        timestamp = int(time.time() * 1000)
        text = "{}{}{}".format(self.app_id, timestamp, self.secret)
        sign = hashlib.md5(text.encode(encoding='UTF-8')).hexdigest()
        url = self.host + f"api/v1/logRecord/updateLog?appId={self.app_id}&sign={sign.upper()}&timestamp={timestamp}"
        logger.info(f'url：{url}')
        logger.info(f'保存参数：{data}')
        res = requests.post(url, json=data)
        logger.info(f'保存结果：{res.text}')
        if res.status_code == 200:
            return res.json()['data']

    # 关闭包裹卡弹窗
    def close_package_dialog(self):
        for c in range(3):
            try:
                if self.page.query_selector('div.disco-shape >> button:has-text("知道了")') is not None:
                    self.page.query_selector('div.disco-shape >> button:has-text("知道了")').click(timeout=3000)
            except:
                pass

    # 关闭广告弹窗
    def close_ad_dialog(self):
        for c in range(3):
            try:
                if self.page.query_selector('div[data-log_element_key="common_goods_push_self_cancel"]:visible').click() is not None:
                    self.page.query_selector('div[data-log_element_key="common_goods_push_self_cancel"]:visible').click().click(timeout=3000)
                    break
                time.sleep(1)
            except:
                time.sleep(1)

    # 关闭11弹窗
    def close_eve_dialog(self):
        for c in range(3):
            try:
                svg_list = self.page.query_selector_all('svg:visible')
                for svg in svg_list:
                    class_name = svg.get_attribute('class')
                    if class_name is not None and 'style_closeIcon' in class_name:
                        svg.click(timeout=3000)
                        break
                time.sleep(1)
            except:
                time.sleep(1)

    # 关闭体验分升级规则弹窗
    def close_level_dialog(self):
        for c in range(3):
            try:
                if self.page.query_selector('div.ecom-guide-normal-actions >> button:has-text("知道了"):visible') is not None:
                    self.page.query_selector('div.ecom-guide-normal-actions >> button:has-text("知道了"):visible').click(timeout=3000)
                time.sleep(1)
            except:
                pass

    # 关闭浏览器兼容弹窗
    def close_browser_tip(self):
        for i in range(3):
            try:
                if self.page.query_selector('div.auxo-modal-confirm-confirm >> span:has-text("知道了"):visible') is not None:
                    self.page.query_selector('div.auxo-modal-confirm-confirm >> span:has-text("知道了"):visible').click(timeout=3000)
            except:
                time.sleep(1)

    # 点击退出引导弹窗
    def click_sign_dialog(self):
        for i in range(3):
            try:
                if self.page.query_selector('div.ws-arrival-modal-content:visible >> svg') is not None:
                    self.page.query_selector('div.ws-arrival-modal-content:visible >> svg').click(timeout=3000)
                    break
                time.sleep(1)
            except:
                time.sleep(1)

    # 点击整改弹窗
    def click_adjust_dialog(self):
        for i in range(3):
            try:
                if self.page.query_selector('button:has-text("我已知悉"):visible') is not None:
                    self.page.query_selector('button:has-text("我已知悉"):visible').click(timeout=3000)
                    break
                time.sleep(1)
            except:
                time.sleep(1)

    def close_tips(self):
        logger.info("检查是否有弹窗...")
        close_list = self.page.query_selector_all('div.index_union__1bLD3')
        if len(close_list) > 0:
            for close in close_list:
                if close.is_visible():
                    try:
                        close.click(timeout=2000)
                    except:
                        logger.info("点击【div.index_union__1bLD3】按钮失败...")
        tips = self.page.query_selector_all('//span[text()="知道了"]')
        for tip in tips:
            if tip.is_visible():
                try:
                    tip.click(timeout=2000)
                except:
                    logger.info("点击【{}】按钮失败...".format(tip.text_content()))
        tips = self.page.query_selector_all('//span[text()="我知道了"]')
        for tip in tips:
            if tip.is_visible():
                try:
                    tip.click(timeout=2000)
                except:
                    logger.info("点击【{}】按钮失败...".format(tip.text_content()))
        tips = self.page.query_selector_all('//span[text()="我已知悉"]')
        for tip in tips:
            if tip.is_visible():
                try:
                    tip.click(timeout=2000)
                except:
                    logger.info("点击【{}】按钮失败...".format(tip.text_content()))
        logger.info("检查授权页面...")
        time.sleep(4)
        tips = self.page.query_selector_all('span.auxo-modal-close-x')
        for tip in tips:
            if tip.is_visible():
                try:
                    tip.click(timeout=2000)
                except:
                    logger.info("点击【span.auxo-modal-close-x】按钮失败...")
            close_list = self.page.query_selector_all('div.index_union__1bLD3')
            if len(close_list) > 0:
                for close in close_list:
                    if close.is_visible():
                        try:
                            close.click(timeout=2000)
                        except:
                            logger.info("点击【div.index_union__1bLD3】按钮失败...")
        for t in range(5):
            try:
                if self.page.query_selector('#DOUXIAOER_WRAPPER >> div.index_union__1bLD3:visible') is not None:
                    self.page.query_selector('#DOUXIAOER_WRAPPER >> div.index_union__1bLD3').click(timeout=2000)
                    break
                time.sleep(1)
            except:
                pass


    def slide_code(self):
        for i in range(20):
            logger.info("等待验证码出现...")
            # 激活page
            self.page.query_selector('//*[@id="ecomLoginForm"]/section/div[3]/div[1]/div/div/span/input')
            if len(self.page.frames) > 1:
                logger.info("验证码的Frame出现...")
                break
            else:
                time.sleep(1)
        self.page.query_selector('div')
        code_frame = self.page.frame(url='https://rmc.bytedance.com/verifycenter/captcha/v2?*')
        for i in range(10):
            if code_frame is not None:
                break
            time.sleep(1)
            self.page.query_selector('div')
            code_frame = self.page.frame(url='https://rmc.bytedance.com/verifycenter/captcha/v2?*')
        has_code = False
        for i in range(20):
            try:
                if code_frame.query_selector('//*[@id="captcha_verify_image"]') is not None and code_frame.query_selector(
                        '//*[@id="captcha_verify_image"]').is_visible():
                    has_code = True
                    logger.info("验证码出现...")
                    time.sleep(5)
                    break
                else:
                    time.sleep(1)
            except:
                pass
        if not has_code:
            return
        logger.info("出现滑动验证码，开始滑动...")
        for k in range(20):
            try:
                if code_frame is None or code_frame.query_selector('//*[@id="captcha_verify_image"]') is None:
                    break
                code_path = os.path.join(os.getcwd(), 'code.png')
                imageback_path = os.path.join(os.getcwd(), 'imageback.png')
                code_frame.query_selector('//*[@id="captcha_verify_image"]').screenshot(path=code_path)
                code_frame.query_selector('//*[@id="captcha-verify_img_slide"]').screenshot(path=imageback_path)
                time.sleep(0.5)
                dist = int(self.get_slider_xy(code_path, imageback_path))
                box = code_frame.query_selector('div.captcha-slider-btn').bounding_box()
                logger.info("重新滑动...")
                track = self.get_tracks(dist + 15, 1, self.ease_out_quart)
                ix = int(box['x'] + box['width'] // 2)
                iy = int(box['y'] + box['height'] // 2)
                self.page.mouse.move(ix, iy)
                self.page.mouse.down(button='left')
                self.move_to_gap(self.page, ix, iy, track)
                time.sleep(1.5)
                logger.info("滑动完成...")
                time.sleep(5)
                self.page.query_selector('div')
                code_frame = self.page.frame(url='https://rmc.bytedance.com/verifycenter/captcha/v2?*')
            except:
                logger.info(traceback.format_exc())
                # 如果是获取验证码结果失败，直接抛错重试
                if '获取验证码结果失败' in traceback.format_exc():
                    raise Exception(traceback.format_exc())

    def ease_out_quart(self, x):
        return 1 - pow(1 - x, 4)

    def get_tracks(self, distance, seconds, ease_func):
        distance += 0
        tracks = [0]
        offsets = [0]
        for t in numpy.arange(0.0, seconds, 0.1):
            ease = ease_func
            offset = round(ease(t / seconds) * distance)
            tracks.append(offset - offsets[-1])
            offsets.append(offset)
        tracks.extend([-3, -2, -3, -2, -2, -2, -2, -1, -0, -1, -1, -1])
        return tracks

    def move_to_gap(self, page, ix, iy, track):
        """
        慢速移动鼠标
        :param ix:
        :param iy:
        :param track:
        :return:
        """
        f_y = [10, 12, 4, 6, 8, -4, -6, -8]
        mx = ix
        while track:
            x = track.pop(0)
            mx = mx + x
            page.mouse.move(mx, iy + random.choice(f_y))
            time.sleep(0.1)
        page.mouse.up()

    def get_slider_xy(self, code_path, imageback_path):
        api = 'http://api.ttshitu.com/base64'
        with open(code_path, 'rb') as f:
            base64_data = base64.b64encode(f.read())
            b642 = base64_data.decode()
        with open(imageback_path, 'rb') as f:
            base64_data = base64.b64encode(f.read())
            b642_back = base64_data.decode()
        params = {"typeid": "33", "image": b642, "username": self.tj_account['tjName'],
                  "password": self.tj_account['tjPass']}
        headers = {'content-type': 'application/json;charset=UTF-8'}
        result = json.loads(requests.post(api, json=params, headers=headers).text)
        if result['success']:
            return result["data"]["result"].split(",")[0]
        else:
            raise Exception("获取验证码结果失败：【{}】".format(result['message']))

    def wait(self, page, seconds=1):
        try:
            page.wait_for_load_state(state="domcontentloaded")
            time.sleep(seconds)
        except:
            traceback.format_exc()

    def get_tujian_account(self):
        url = self.host + "api/v1/tjAccount/findOne?appId={}&sign={}&timestamp={}"
        timestamp = int(time.time() * 1000)
        text = "{}{}{}".format(self.app_id, timestamp, self.secret)
        sign = hashlib.md5(text.encode(encoding='UTF-8')).hexdigest()
        res = requests.get(url.format(self.app_id, sign.upper(), timestamp))
        if res.status_code == 200:
            logger.info("获取到图鉴：【{}】".format(res.json()['data']))
            return res.json()['data']
        else:
            logger.info("获取图鉴失败：【{}】".format(res.json()))
            return None

    def close(self):
        try:
            if self.page:
                self.page.close()
        except:
            pass
        try:
            if self.browser:
                self.browser.close()
        except:
            pass
        try:
            if self.context:
                self.context.close()
        except:
            pass

    def check_sms_code(self, mobile):
        has_sms = False
        for c in range(5):
            if self.page.query_selector('input[fieldname="item_label_verify_code"]:visible') is not None:
                has_sms = True
                break
            logger.info(f'正在检查是否需要手机验证码【{c + 1}】秒')
            time.sleep(1)
        logger.info(f'检查结果：【{has_sms}】')
        if has_sms:
            self.page.query_selector('div.account-center-code-captcha:visible').click()
            time.sleep(30)
            sms_text = self.get_sms_code(mobile)
            if '抖店' not in sms_text:
                logger.info('获取验证码失败')
                raise Exception('获取验证码失败')
            # 验证码示例：【抖店】验证码1234，......。
            res = re.findall('\d+', sms_text.split('，')[0])
            sms_code = res[0]
            self.page.query_selector('input[title="验证码"]').fill(sms_code)
            time.sleep(1)
            self.page.query_selector('button:has-text("验证")').click()

    # 获取手机验证码
    def get_sms_code(self, mobile):
        for t in range(3):
            try:
                res = requests.get(f'http://1.95.166.150:8997/api/v1/sms/info/{mobile}')
                logger.info(f'获取手机验证码结果：{res.text}')
                sms_text = res.json()['data']
                if sms_text is not None and '抖店' in sms_text:
                    return sms_text
                time.sleep(3)
            except:
                logger.info(traceback.format_exc())
        return ''

    def encode_param(self, data_list):
        timestamp = int(time.time() * 1000)
        text = "{}{}{}".format(self.app_id, timestamp, self.secret)
        sign = hashlib.md5(text.encode(encoding='UTF-8')).hexdigest()
        url = self.host + f"api/v1/storeAccount/codingParams?appId={self.app_id}&sign={sign.upper()}&timestamp={timestamp}"
        res = requests.post(url, data=json.dumps(data_list), headers={'content-type': 'application/json'})
        if res.status_code == 200:
            return res.json()['data']

    def upload_file(self, uri, coding_param, file_list):
        try:
            timestamp = int(time.time() * 1000)
            text = "{}{}{}".format(self.app_id, timestamp, self.secret)
            sign = hashlib.md5(text.encode(encoding='UTF-8')).hexdigest()
            logger.info("正在上传文件到服务器")
            url = self.host + uri + f"?appId={self.app_id}&sign={sign.upper()}&timestamp={timestamp}"
            files = []
            for file in file_list:
                files.append(('file', (open(file, 'rb'))))
            logger.info(f'上传文件到服务器url：{url}')
            response = requests.request("POST", url, files=files, headers={'codingParam': coding_param}, timeout=180)
            logger.info('上传文件到服务器结果：' + response.text)
        except:
            logger.info(traceback.format_exc())

    # 切换店铺
    def change_store(self, account_obj):
        has_store = False
        logger.info(f'切换店铺【{account_obj["account"]}】')
        self.page.query_selector('div.headerShopName').click()
        time.sleep(1)
        self.page.query_selector('div:text-is("切换组织/店铺")').click()
        time.sleep(5)
        store_list = []
        div_list = self.page.query_selector_all('div')
        for div in div_list:
            class_name = div.get_attribute('class')
            if class_name is not None and 'index_introName__' in class_name:
                store_list.append(div)
        for store in store_list:
            if store.text_content() == account_obj['account']:
                store.click()
                self.wait(self.page)
                logger.info("登录店铺...")
                has_store = True
                break
        return has_store

    def get_login_account(self):
        try:
            post_url = self.host + "api/v1/storeAccount/getAccountList/DY?type=结算账单,资金流水,结算账单总数,资金流水总数"
            timestamp = int(time.time() * 1000)
            text = "{}{}{}".format(self.app_id, timestamp, self.secret)
            sign = hashlib.md5(text.encode(encoding='UTF-8')).hexdigest()
            response = requests.get(post_url + "&appId={}&sign={}&timestamp={}".format(self.app_id, sign.upper(), timestamp))
            if response.status_code == 200:
                logger.info("获取抖音平台的账号密码信息成功")
                accounts = response.json()
                logger.info(accounts['data'])
                return accounts['data']
            else:
                logger.info("获取抖音平台的账号密码信息失败")
        except:
            traceback.format_exc()
        return []

    def get_job_id(self, key):
        timestamp = int(time.time() * 1000)
        text = "{}{}{}".format(self.app_id, timestamp, self.secret)
        sign = hashlib.md5(text.encode(encoding='UTF-8')).hexdigest()
        url = self.host + f"api/v1/storeAccount/getJobId/{key}?appId={self.app_id}&sign={sign.upper()}&timestamp={timestamp}"
        res = requests.get(url)
        logger.info(res.text)
        if res.status_code == 200:
            return res.json()['data']

    def get_job_list(self, store_id):
        timestamp = int(time.time() * 1000)
        text = "{}{}{}".format(self.app_id, timestamp, self.secret)
        sign = hashlib.md5(text.encode(encoding='UTF-8')).hexdigest()
        url = self.host + f"api/v1/storeAccount/getJobId/{key}?appId={self.app_id}&sign={sign.upper()}&timestamp={timestamp}"
        res = requests.get(url)
        logger.info(res.text)
        if res.status_code == 200:
            return res.json()['data']

    def close(self):
        try:
            if self.page:
                self.page.close()
        except:
            pass
        try:
            if self.browser:
                self.browser.close()
        except:
            pass
        try:
            if self.context:
                self.context.close()
        except:
            pass